#include "CentralCache.h"
#include "PageCache.h"

CentralCache CentralCache::_sInst;

// 从SpanList或者page cache中获取一个非空的span
Span *CentralCache::GetOneSpan(SpanList &list, size_t size)
{
    // 首先判断当前的SpanList是否有非空的span
    Span *it = list.Begin();
    while (it != list.End())
    {
        if (it->_freeList != nullptr)
        {
            return it;
        }
        else
        {
            it = it->_next;
        }
    }

    // 走到这说明需要从page cache中获取非空的span

    // 先解除桶锁, 方便thread cache释放内存回到该桶中不会被阻塞

    list._mtx.unlock();

    PageCache::GetInstance()->_pageMtx.lock();
    Span *span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
    PageCache::GetInstance()->_pageMtx.unlock();

    // 此时还不需要加桶锁, 因为刚获取的span别的线程暂时访问不到

    // 计算该span的地址和大小
    char *start = (char *)(span->_pageId << PAGE_SHIFT);
    size_t bytes = span->_n << PAGE_SHIFT;
    char *end = start + bytes;

    // 将获取的span进行切分
    // 先切一块下来做头, 方便尾插
    span->_freeList = start;
    start += size;
    void *tail = span->_freeList;
    while (start < end)
    {
        NextObj(tail) = start;
        tail = start;
        start += size;
    }
    NextObj(tail) = nullptr;

    list._mtx.lock();
    list.PushFront(span);

    return span;
}

// 从central cache中获取一定数量的内存对象
size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);

    _spanLists[index]._mtx.lock();

    Span *span = GetOneSpan(_spanLists[index], size);
    assert(span);
    assert(span->_freeList);

    start = span->_freeList;
    end = start;
    size_t actualNum = 1;
    size_t i = 0;
    while (i < batchNum - 1 && NextObj(end) != nullptr)
    {
        end = NextObj(end);
        i++;
        actualNum++;
    }
    span->_freeList = NextObj(end);
    NextObj(end) = nullptr;

    _spanLists[index]._mtx.unlock();

    return actualNum;
}

// 将一定数量的内存对象释放回span跨度
void CentralCache::ReleaseListToSpans(void *start, size_t size)
{
    assert(start);

    size_t index = SizeClass::Index(size);

    _spanLists[index]._mtx.lock();

    while (start)
    {
        void* next = NextObj(start);

        Span *span = PageCache::GetInstance()->MapObjectToSpan(start);
        NextObj(start) = span->_freeList;
        span->_freeList = start;
        span->_useCount--;

        if (span->_useCount == 0)
        {
            // 表征该span空闲, page cache回收该span并尝试做前后页span的合并
            _spanLists[index].Erase(span);
            span->_freeList = nullptr;
            span->_prev = nullptr;
            span->_next = nullptr;

            // 解除桶锁, 方便其他线程向该桶中申请或释放内存对象, 只使用page cache一把锁即可
            _spanLists[index]._mtx.unlock();
            PageCache::GetInstance()->_pageMtx.lock();
            PageCache::GetInstance()->ReleaseSpanToPageCache(span);
            PageCache::GetInstance()->_pageMtx.unlock();
            _spanLists[index]._mtx.unlock();
        }
        start = next;
    }

    _spanLists[index]._mtx.unlock();
}